#pragma once
#include <iostream>
#include <sstream>
#include <string>
#include <vector>

class CString : public std::string {
public:
    using std::string::string;// derive base constructor
    using std::string::replace;// derive this function' all overloads

    CString(const char* str) : std::string(str) {}

    CString(const std::string& str) : std::string(str) {}

    int indexOf(const std::string& s, int startIndex = 0) {
        size_t pos = this->find(s, startIndex);
        return (pos != std::string::npos) ? static_cast<int>(pos) : -1;
    }

    int lastIndexOf(const std::string& subStr) const {
        size_t pos = this->rfind(subStr);
        if (pos != std::string::npos)
            return static_cast<int>(pos);
        return -1;
    }

    int indexOf(char c, int startIndex = 0) const {
        size_t pos = this->find(c, startIndex);
        return (pos != std::string::npos) ? static_cast<int>(pos) : -1;
    }

    int lastIndexOf(char c) const {
        size_t pos = this->rfind(c);
        return (pos != std::string::npos) ? static_cast<int>(pos) : -1;
    }

    void replace(const std::string& _old, const std::string& _new, bool replaceAll = false) {
        size_t pos = 0;
        while ((pos = this->find(_old, pos)) != std::string::npos) {
            this->std::string::replace(pos, _old.length(), _new);// use std::string::replace
            if (!replaceAll)
                break;
            pos += _new.length();// Move past the replacement
        }
    }

    bool contains(const std::string& substr) const {
        return this->find(substr) != std::string::npos;
    }

    CString subString(size_t startIndex, size_t len = std::string::npos) const {
        if (startIndex < 0 || startIndex >= this->size())
            return "";
        if (startIndex + len > this->size())
            return this->substr(startIndex);
        return this->substr(startIndex, len);
    }

    std::vector<CString> split(const std::string& s) {
        std::vector<CString> result;
        size_t pos = 0;
        size_t delimiterPos;
        while ((delimiterPos = this->find(s, pos)) != std::string::npos) {
            result.push_back(CString(this->substr(pos, delimiterPos - pos)));
            pos = delimiterPos + s.length();
        }
        result.push_back(CString(this->substr(pos)));// last part
        return result;
    }

    std::vector<CString> split(char s) const {
        std::vector<CString> result;
        size_t pos = 0;
        size_t delimiterPos;
        while ((delimiterPos = this->find(s, pos)) != std::string::npos) {
            result.push_back(CString(this->substr(pos, delimiterPos - pos)));
            pos = delimiterPos + 1;
        }
        result.push_back(CString(this->substr(pos)));
        return result;
    }

    template<typename T>
    CString& append(T t) {
        std::ostringstream ss;
        ss << t;
        *this += ss.str();
        return *this;
    }

    template<typename T>
    CString& operator<<(const T& value) {
        return append(value);
    }

    /**
    * @example CString s = CString("Name: %1, Age: %2").arg("akuan").arg(20); => s = "Name: akuan, Age: 20"
    */
    template<typename T>
    CString arg(const T& value) const {
        CString result = *this;
        static const std::string placeholderFormat = "%%{}";// %N

        for (int i = 1; i <= 9; ++i) {// assume max %9
            std::ostringstream placeholder;
            placeholder << "%" << i;
            std::string currentPlaceholder = placeholder.str();
            size_t pos = result.find(currentPlaceholder);
            if (pos != std::string::npos) {
                // use ostringstream to translate any type to string
                std::ostringstream stream;
                stream << value;
                std::string valueStr = stream.str();
                result.replace(pos, currentPlaceholder.length(), valueStr);
                break;
            }
        }
        return result;
    }

    template<typename T>
    static CString number(const T& value) {
        return std::to_string(value);
    }

    static CString fromCharArray(const std::vector<char>& charArr) {
        return std::string(charArr.begin(), charArr.end());// or: std::string(charArr.data(), charArr.size());
    }

    std::string toStdString() const {
        return static_cast<std::string>(*this);
    }

    friend std::ostream& operator<<(std::ostream& os, const CString& str) {// for plog
        os << static_cast<std::string>(str);
        return os;
    }

#define LOGIC std::ostringstream oss; oss << lhs << rhs; return oss.str();

    friend CString operator+(const CString& lhs, int rhs) { LOGIC }
    friend CString operator+(const CString& lhs, double rhs) { LOGIC }
    friend CString operator+(const CString& lhs, const char* rhs) { LOGIC }
    friend CString operator+(const std::string& lhs, const std::string& rhs) { LOGIC }
    friend CString operator+(int lhs, const CString& rhs) { LOGIC }
    friend CString operator+(double lhs, const CString& rhs) { LOGIC }
    friend CString operator+(const char* lhs, const CString& rhs) { LOGIC }
};